﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MathNet.Numerics.LinearAlgebra.Double;
using MathNet.Numerics.LinearAlgebra.Generic;
using NeuralNetworkPro.Activation_Functions;

namespace NeuralNetworkPro
{
    public static class Utils
    {
        public static string BackPropagationHeader { get { return "#BackPropagationData"; } }
        public static string NetworkHeader { get { return "#NetworkData"; } }
        public static List<string> ToCSVStrings(this double[][][] data)
        {
            var res = new List<string>();
            foreach (var mat in data)
            {
                var mat_s = "";
                foreach (var vect in mat)
                {
                    foreach (var val in vect)
                    {
                        mat_s += val + ";";
                    }
                    mat_s += "@";
                }
                res.Add(mat_s);
            }
            return res;
        }
        public static double[][][] FromCSVStrings(this List<string> data)
        {
            var res = new double[data.Count][][];
            for (int i = 0; i < data.Count; i++)
            {
                var mat = data[i].Split(new char[] { '@' }, StringSplitOptions.RemoveEmptyEntries);
                res[i] = new double[mat.Length][];
                for (int j = 0; j < mat.Length; j++)
                {
                    var vect = mat[j].Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
                    res[i][j] = new double[vect.Length];
                    for (int k = 0; k < vect.Length; k++)
                    {
                        res[i][j][k] = double.Parse(vect[k]);
                    }
                }
            }
            return res;
        }
        public static IActivationFunction DeserializeFunction(string data)
        {
            var vals = data.Split(new char[] { ':' });
            if (vals[0] == BipolarSigmoidFunction.FunctionName)
                return new BipolarSigmoidFunction(double.Parse(vals[1]));
            if (vals[0] == HyperTangentFunction.FunctionName)
                return new HyperTangentFunction();
            if (vals[0] == IdentityFunction.FunctionName)
                return new IdentityFunction();
            if (vals[0] == SigmoidFunction.FunctionName)
                return new SigmoidFunction(double.Parse(vals[1]));
            if (vals[0] == ThresholdFunction.FunctionName)
                return new ThresholdFunction();
            else
                throw new Exception("Error in function deserialization");
        }
        public static Tuple<int, int> IndexOfMin<T>(this T[][] source, Func<T, double> selector)
        {
            if (source == null)
                throw new ArgumentNullException("source");

            var minValue = double.MaxValue;
            int fst_index = -1;
            int snd_index = -1;

            for (int i = 0; i < source.Length; i++)
            {
                for (int j = 0; j < source[i].Length; j++)
                {
                    try
                    {
                        var num = selector.Invoke(source[i][j]);

                        if (num <= minValue)
                        {
                            minValue = num;
                            fst_index = i;
                            snd_index = j;
                        }
                    }
                    catch (Exception) { }
                }
            }

            if (fst_index == -1)
                throw new InvalidOperationException("Sequence was empty");

            return new Tuple<int, int>(fst_index, snd_index);
        }

        public static IEnumerable<T> RemoveIdx<T>(this IEnumerable<T> item, int idx)
        {
            return item.Take(idx).Concat(item.Skip(idx + 1));
        }

        public static double ScalarProduct(this Vector<double> a, Vector<double> b)
        {
            if (a.Count != b.Count)
                throw new Exception("Dimensions must be equal");
            double res = 0;
            for (int i = 0; i < a.Count; i++)
            {
                res += a[i] * b[i];
            }

            return res;
        }
    }
}
